﻿namespace MSSqlSchemaDoc.Provider.MicrosoftSqlServer
{
    using System;
    using System.Collections.ObjectModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Text;
    using MSSqlSchemaDoc.Core;
    using MSSqlSchemaDoc.Core.DataStructures;
    using MSSqlSchemaDoc.Provider.MicrosoftSqlServer.Helpers;
    using MSSqlSchemaDoc.Provider.MicrosoftSqlServer.Resources;

    /// <summary>
    /// Contains the helper methods for stored procedure objects.
    /// </summary>
    public partial class SqlScriptingManager
    {
        /// <summary>
        /// Construct a stored procedure object.
        /// </summary>
        /// <param name="reader">Use the data in the data reader starting at it's current position.</param>
        /// <param name="hasMoreProcedures">This method will return false if this is the last table in the result set.</param>
        /// <returns>The constructed procedure.</returns>
        private StoredProcedure ConstructStoredProcedure(IDataReader reader, ref bool hasMoreProcedures)
        {
            // Create the table object and set properties
            StoredProcedure p = new StoredProcedure()
            {
                Name = DataReaderHelper.GetString(reader, "ObjectName"),
                Schema = DataReaderHelper.GetString(reader, "SchemaName")
            };

            p.ObjectId = DataReaderHelper.GetInt(reader, "ObjectId").Value;

            p.Definition = DataReaderHelper.GetString(reader, "Definition");
            p.IsAutoExecuted = DataReaderHelper.GetBool(reader, "IsAutoExecuted");
            p.IsExecutionReplicated = DataReaderHelper.GetBool(reader, "IsExecutionReplicated");
            p.IsReplicationSerializableOnly = DataReaderHelper.GetBool(reader, "IsReplicationSerializableOnly");
            p.SkipsReplicationConstraints = DataReaderHelper.GetBool(reader, "SkipsReplicationConstraints");
            p.UsesAnsiNulls = DataReaderHelper.GetBool(reader, "UsesAnsiNulls");
            p.UsesQuotedIdentifier = DataReaderHelper.GetBool(reader, "UsesQuotedIdentifier");
            p.IsSchemaBound = DataReaderHelper.GetBool(reader, "IsSchemaBound");
            p.UsesDatabaseCollation = DataReaderHelper.GetBool(reader, "UsesDatabaseCollation");
            p.IsRecompiled = DataReaderHelper.GetBool(reader, "IsRecompiled");
            p.ExecuteAsPrincipalId = DataReaderHelper.GetInt(reader, "ExecuteAsPrincipalId");

            // Add the parameters if any exist
            if (String.IsNullOrEmpty(DataReaderHelper.GetString(reader, "ParameterName")))
            {
                // Advance the reader to the next procedure
                hasMoreProcedures = reader.Read();
            }
            else
            {
                p.Parameters = new Collection<ProcedureParameter>();
                p.Parameters.Add(ConstructProcedureParameter(reader));

                // Get all the parameter for the procedure by looping through the data reader
                string currentId = reader["ObjectId"].ToString();
                while ((hasMoreProcedures = reader.Read()) && currentId == reader["ObjectId"].ToString())
                {
                    p.Parameters.Add(ConstructProcedureParameter(reader));
                }
            }

            // Documentation
            if (this.IncludeDocumentationObjects)
            {
                p.Documentation = this.ConstructStoredProcedureDocumentation(p);
            }

            return p;
        }

        /// <summary>
        /// Fetches a list of stored procedures and extracts the information for each.
        /// </summary>
        /// <param name="connection">An open connection to the database.</param>
        /// <param name="objectType">The stored procedure object type.</param>
        private void ScriptStoredProcedures(SqlConnection connection, DataStructureObjectType objectType)
        {
            // Load data
            StringBuilder query = new StringBuilder(this.queryManager.StoredProcedures());
            this.InsertWhereClause(query);
            SqlCommand c = new SqlCommand(query.ToString(), connection);

            using (IDataReader reader = c.ExecuteReader())
            {
                // Build the stored procedures
                bool hasData;
                while (hasData = reader.Read())
                {
                    while (hasData)
                    {
                        string name = GetObjectName(reader);
                        this.OnBeginScriptingObject(name);

                        StoredProcedure p = this.ConstructStoredProcedure(reader, ref hasData);
                        this.OnCompleteScriptingObject(p, name);
                    }
                }
            }
        }

        /// <summary>
        /// Constructs the documentation object for a procedure.
        /// </summary>
        /// <param name="procedure">The procedure to document.</param>
        /// <returns>The documentation object.</returns>
        private StoredProcedureDocumentation ConstructStoredProcedureDocumentation(StoredProcedure procedure)
        {
            StoredProcedureDocumentation existingDocumentation;
            StoredProcedureDocumentation documentation = this.ConstructDocumentationObject<StoredProcedureDocumentation>(out existingDocumentation, procedure.Name, procedure.Schema);

            // Parameters
            foreach (ProcedureParameter p in procedure.Parameters)
            {
                ParameterDocumentation pd = new ParameterDocumentation() { Name = p.Name };
                pd.Documentation = GetDocumentationFromCollection<ParameterDocumentation>(existingDocumentation.Parameters, pd.Name, pd.Schema);
                documentation.Parameters.Add(pd);
            }

            return documentation;
        }
    }
}
